package com.learn.security.web.filters;

import com.learn.security.common.VerifyConstant;
import com.learn.security.service.impl.UserDetailService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.authentication.AuthenticationProvider;
import org.springframework.security.authentication.BadCredentialsException;
import org.springframework.security.authentication.DisabledException;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.crypto.password.NoOpPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import org.thymeleaf.util.StringUtils;

import javax.servlet.http.HttpServletRequest;
import java.util.Objects;

/**
 * @auther liuhw
 * @date 2020/3/22 17:10
 * @description:
 * @since 1.0
 **/
@Component
public class VerifyAuthenticationProvider implements AuthenticationProvider {
    @Autowired
    private UserDetailService userDetailsService;

    @Autowired(required = false)
    private PasswordEncoder passwordEncoder;

    @Override
    public Authentication authenticate(Authentication authentication) throws AuthenticationException {
        // 获取用户输入的用户名和密码
        String inputName = authentication.getName();
        String inputPassword = authentication.getCredentials().toString();

        VerifyWebAuthenticationDetails authenticationDetails = (VerifyWebAuthenticationDetails) authentication.getDetails();
        String verify = authenticationDetails.getVerifyCode();

        if (!this.validateVerify(verify)) {
            throw new DisabledException("验证码输入错误");
        }

        // userDetails为数据库查询到的用户信息
        UserDetails userDetails = userDetailsService.loadUserByUsername(inputName);

        // 自定义的AuthenticationProvider需要自行验证密码
        if (Objects.isNull(userDetails) || !this.getPasswordEncoder().matches(inputPassword, userDetails.getPassword())) {
            throw new BadCredentialsException("用户名或密码错误");
        }

        return new UsernamePasswordAuthenticationToken(inputName, inputPassword, userDetails.getAuthorities());
    }

    @Override
    public boolean supports(Class<?> authentication) {
        return authentication.equals(UsernamePasswordAuthenticationToken.class);
    }

    private boolean validateVerify(String verify) {
        // 获取当前线程绑定的request对象
        HttpServletRequest request = ((ServletRequestAttributes) Objects.requireNonNull(RequestContextHolder.getRequestAttributes())).getRequest();
        // 不区分大小写
        // 这个validateCode 是 servlet 中存入session的名字
        String validateCode = String.valueOf(request.getSession().getAttribute(VerifyConstant.VERIFY_CODE_NAME));

        return StringUtils.equalsIgnoreCase(validateCode, verify);
    }

    private PasswordEncoder getPasswordEncoder() {
        if (Objects.isNull(passwordEncoder)) {
            passwordEncoder = NoOpPasswordEncoder.getInstance();
        }
        return passwordEncoder;
    }
}
